import "./css/popup.css";
import WKT from "wkt";
import * as turf from "@turf/turf";
import Popup from "./lib/Popup.js";
import CustomTilingScheme from "./lib/CustomTilingScheme.js";
import { Particle3D } from "./lib/windy/index.js";

import "./lib/MVTImageryProvider.js";
import "./lib/CircleWaveMaterialProperty.js";
import "./lib/EllipsoidFadeMaterial.js";
import "./lib/SpritelineMaterialProperty.js";
import "./lib/LineFlickerMaterialProperty.js";
import "./lib/LineFlowMaterialProperty.js";
import "./lib/DynamicWallMaterialProperty.js";
import "./lib/WallDiffuseMaterialProperty.js";
import "./lib/LineTrailMaterial.js";
import "./lib/ViewShed.js";
import "./lib/VideoShed.js";
import "./lib/ViewController.js";
import "./lib/CesiumMeasure.js";
import "./lib/CesiumDrawer.js";
class MapClass {
  constructor(container, mapConfig) {
    this.container = container;
    this.mapConfig = mapConfig;
    // 初始化
    this.init();
  }

  init() {
    let { defaultToken, projection, isTerrain, sceneMode } = this.mapConfig;
    // 设置cesium默认token
    Cesium.Ion.defaultAccessToken = defaultToken;
    this.viewer = new Cesium.Viewer(this.container, {
      animation: false, // 是否显示动画控件
      shouldAnimate: true,
      homeButton: false, // 是否显示Home按钮
      fullscreenButton: false, // 是否显示全屏按钮
      baseLayerPicker: false, // 是否显示图层选择控件
      geocoder: false, // 是否显示地名查找控件
      timeline: false, // 是否显示时间线控件
      sceneModePicker: false, // 是否显示投影方式控件
      navigationHelpButton: false, // 是否显示帮助信息控件
      selectionIndicator: false, //去掉选中绿框
      infoBox: false, // 是否显示点击要素之后显示的信息
      requestRenderMode: true, // 启用请求渲染模式
      scene3DOnly: (sceneMode === undefined || sceneMode === 3), // 每个几何实例将只能以3D渲染以节省GPU内存
      sceneMode: sceneMode || 3, // 初始场景模式 1、 3D平面模式 2、 2D模式 3、 3D模式
      imageryProvider: this.createImageryLayer(this.mapConfig.baseLayer), //设置底图
      terrainProvider: isTerrain ? this.createTerrain() : null, //地形
      mapProjection: projection ? new Cesium[projection + "Projection"]() : null, //地图坐标系
    });
    // 设置缩放距离控制
    this.viewer.scene.screenSpaceCameraController.minimumZoomDistance = this.mapConfig.mapOption.minDistance || 15;
    if(this.mapConfig.mapOption.maxDistance) {
      this.viewer.scene.screenSpaceCameraController.maximumZoomDistance = this.mapConfig.mapOption.maxDistance;
    }
    //是否开启抗锯齿
    if (Cesium.FeatureDetection.supportsImageRenderingPixelated()) {
      this.viewer.resolutionScale = window.devicePixelRatio
    }
    this.viewer.scene.fxaa = true;
    this.viewer.scene.postProcessStages.fxaa.enabled = true;
    // 开启深度检测
    // this.viewer.scene.globe.depthTestAgainstTerrain = true;
    // 是否显示光圈
    this.viewer.scene.skyAtmosphere.show = true;
    // 隐藏版权
    this.viewer._cesiumWidget._creditContainer.style.display = "none";
    // 开启模型自定义渲染
    Cesium.ExperimentalFeatures.enableModelExperimental = true;
    // 取消双击追踪物体
    this.viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
    // 添加弹窗
    this.popup = new Popup(this.viewer);
    // 相机视角模式控制
    this.viewController = new Cesium.ViewController(this.viewer);
    // 测量工具
    this.measure = new Cesium.Measure(this.viewer);
    // 绘制工具
    this.drawer = new Cesium.Drawer(this.viewer);
  }
  // 获取当前地图缩放级别
  getZoom() {
    let height = this.viewer.camera.positionCartographic.height;
    let A = 40487.57;
    let B = 0.00007096758;
    let C = 91610.74;
    let D = -40467.74;
    return Math.round(D + (A - D) / (1 + Math.pow(height / C, B)));
  }
  /**
   * 隐藏地球等环境因素
   */
  hideEnvironment(options = {}) {
    this.viewer.scene.sun.show = false; //隐藏太阳
    this.viewer.scene.moon.show = false; //隐藏月亮
    this.viewer.scene.skyBox.show = false; //关闭天空盒
    this.viewer.scene.skyAtmosphere.show = false;//关闭光圈
    // this.viewer.scene.undergroundMode = true; //重要，开启地下模式，设置基色透明
    this.viewer.scene.globe.show = !!options.showEarth; //不显示地球
  }
  /**
   * 开启地形深度检测
   * @param {*} bool 
   */
  openTerrainDeepTest(bool) {
    this.viewer.scene.globe.depthTestAgainstTerrain = bool;
  }
  /**
   * 禁用鼠标操作
   * options {
   *  rotate //左键拖动
   *  tilt //中键旋转
   *  zoom //滚轮缩放
   * }
   */
  stopOperation(options) {
    !options.tilt && (this.viewer.scene.screenSpaceCameraController.tiltEventTypes = []);
    this.viewer.scene.screenSpaceCameraController.enableRotate = options.rotate || false; //左键拖动
    this.viewer.scene.screenSpaceCameraController.enableTilt = options.tilt || false; //中键旋转
    this.viewer.scene.screenSpaceCameraController.enableZoom = options.zoom || false; //滚轮缩放
  }
  /**
   * 添加天空盒
   * @param {Object} sources 图片资源
   */
  addSkyBox(sources) {
    this.viewer.scene.skyBox = new Cesium.SkyBox({
      sources
    });
  }
  /**
   * 创建默认地形数据
   */
  createTerrain(url, options) {
    return new Cesium.CesiumTerrainProvider({url: url || 'http://data.marsgis.cn/terrain', ...options});
  }
  /**
   * 添加影像图层
   * option
   * @param {String} type ["UrlTemplate", "ArcGisMapServer", "Mapbox"]
   * @param {String} url 
   * @param {String} sr 
   */
  addImageryLayer(option) {
    return this.viewer.imageryLayers.addImageryProvider(this.createImageryLayer(option));
  }
  /**
   * 删除影像图层
   * @param {ImageryProvider} imageryLayer
   */
  removeImageryLayer(imageryLayer) {
    return this.viewer.imageryLayers.remove(imageryLayer);
  }
  /**
   * 创建影像图层
   * @param {Object} option
   */
  createImageryLayer(option) {
    if(option.sr) {
      option.tilingScheme = new CustomTilingScheme({
        fromSR: option.sr,
        toSR: "EPSG:4326"
      });
    }
    return new Cesium[option.type + 'ImageryProvider'](option);
  }
  /**
   * 隐藏基础底图
   * @param {Boolean} bool 
   */
  hideBaseLayer(bool) {
    this.viewer.imageryLayers.get(0).show = !bool;
  }
  /**
   * 自动旋转
   * @param {Function} callback 旋转结束后回调
   * @param {Number} time 旋转时间（毫秒）
   */
  autoRotation(callback, time) {
    let i = Date.now();
    let rotate = () => {
      var a = 0.8;
      var t = Date.now();
      var n = (t - i) / 1e3;
      i = t;
      this.viewer.scene.camera.rotate(Cesium.Cartesian3.UNIT_Z, -a * n);
    }
    this.viewer.clock.onTick.addEventListener(rotate);
    setTimeout(() => {
      this.viewer.clock.onTick.removeEventListener(rotate);
      callback();
    }, time);
  }
  /**
   * 开启卷帘效果
   */
  startSwipe(swipeDom) {
    let viewer = this.viewer;
    if(!swipeDom) {
      // 获取当前地图容器下的swipe dom，防止多地图情况下，获取swipe dom问题
      this.container.childNodes.forEach(item => {
        if(item.className === "swipe-container") {
          swipeDom = item;
        }
      });
    }
    swipeDom.style.display = "block";
    this.swipeDom = swipeDom;
    let splitPosition = swipeDom.offsetLeft / swipeDom.parentElement.offsetWidth;
    viewer.scene.splitPosition = splitPosition; // 设置卷帘左右分区范围(0-1)之间
    let obj = {};
    swipeDom.onmousedown = function(event) {
      let e = event || window.event; 
      // 鼠标点击元素那一刻相对于元素左侧边框的距离=点击时的位置相对于浏览器最左边的距离-物体左边框相对于浏览器最左边的距离
      obj.diffX = e.clientX - this.offsetLeft;
      document.onmousemove = function(event) {
        let e = event || window.event;
        let moveX = e.clientX - obj.diffX;
        if (moveX < 0) {
          moveX = 0
        } else if (moveX > swipeDom.parentElement.offsetWidth - swipeDom.offsetWidth / 2) {
          moveX = swipeDom.parentElement.offsetWidth - swipeDom.offsetWidth / 2
        }
        swipeDom.style.left = moveX + 'px';
        let splitPosition = moveX / swipeDom.parentElement.offsetWidth;
        viewer.scene.splitPosition = splitPosition; // 设置卷帘左右分区范围(0-1)之间
      };
      document.onmouseup = function() {
        this.onmousemove = null;
        this.onmouseup = null;
      }
    };
  }
  /**
   * 关闭卷帘效果
   */
  closeSwipe() {
    if(this.swipeDom) {
      this.swipeDom.style.display = "none";
      this.viewer.scene.splitPosition = 0;
    }
  }
  /**
   * 视角飞行定位
   * @param {Object} options {
   *  lng: Number, //经度
   *  lat: Number,//纬度
   *  height: Number = 0,//高度
   *  heading: Number = 0,//偏航角
   *  pitch: Number = -45, //俯仰角
   *  distance: Number = 500,//距离
   *  duration: Number = 3,//持续时间
   * }
   */
  flyTo(options) {
    return new Promise((resolve, reject) => {
      let center = Cesium.Cartesian3.fromDegrees(options.lng, options.lat, options.height || 0);
      let heading = Cesium.Math.toRadians(options.heading || 0);
      let pitch = Cesium.Math.toRadians(options.pitch || -45);
      this.viewer.camera.flyToBoundingSphere(new Cesium.BoundingSphere(center, 1), {
        offset: new Cesium.HeadingPitchRange(heading, pitch, options.distance || 500),
        duration: options.duration === 0 ? options.duration : (options.duration || 3),
        complete() {
          resolve(true);
        },
        cancel() {
          reject(false);
        }
      });
    });
  }
  /**
   * 视角定位默认位置
   */
  flyToHome(options = {}) {
    let mapOption = {...this.mapConfig.mapOption, ...options};
    return this.flyTo(mapOption);
  }
  /**
   * 视角定位物体
   * @param {Object} entity 
   * @param {Object} options {
   *  heading: Number = 0,//偏航角
   *  pitch: Number = -45, //俯仰角
   *  distance: Number = 500,//距离
   *  duration: Number = 3,//持续时间
   * }
   * @returns Promise<true> 使用.then()定位结束
   */
  flyToEntity(entity, options = {}) {
    let offset;
    if(options.heading || options.pitch || options.distance) {
      let heading = Cesium.Math.toRadians(options.heading || 0);
      let pitch = Cesium.Math.toRadians(options.pitch || -45);
      offset = new Cesium.HeadingPitchRange(heading, pitch, options.distance);
    }
    return this.viewer.flyTo(entity, {
      duration: options.duration,
      offset
    });
  }
  /**
   * 视角固定到物体
   */
  lookAt(target, options = {}) {
    if(!target) { 
      //取消视角固定
      this.viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
      return;
    }
    let center;
    if(target instanceof Cesium.Cartesian3) {
      center = target;
    } else if(target instanceof Cesium.Entity && target.position) {
      center = target.position.getValue();
    }
    if(center) {
      let heading = Cesium.Math.toRadians(options.heading || 0);
      let pitch = Cesium.Math.toRadians(options.pitch || -45);
      // 视角锁定物体，距离物体22m时，无法再放大
      let offset = new Cesium.HeadingPitchRange(heading, pitch, options.distance || 22);
      this.viewer.camera.lookAt(center, offset);
    }
  }
  /**
   * 视角追踪物体，并且可以操作相机
   */
  trackEntity(entity) {
    if(!entity) {
      // 取消视角追踪
      this.viewer.trackedEntity = undefined;
      return;
    }
    this.viewer.trackedEntity = entity;
  }
  // 地图放大
  zoomIn() {
    let position = this.viewer.camera.position;
    let cameraHeight = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(position).height;
    let minDistance = this.viewer.scene.screenSpaceCameraController.minimumZoomDistance;
    if(cameraHeight > minDistance) {
      // 每次缩小 20 倍，参数可改  
      let moveRate = cameraHeight / 4;
      this.viewer.camera.moveForward(moveRate);
    }
  }
  // 地图缩小
  zoomOut() {
    let position = this.viewer.camera.position;
    let cameraHeight = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(position).height;
    let maxDistance = this.viewer.scene.screenSpaceCameraController.maximumZoomDistance;
    if(cameraHeight < maxDistance) {
      // 每次缩小 20 倍，参数可改  
      let moveRate = cameraHeight / 4;
      this.viewer.camera.moveBackward(moveRate);
    }
  }
  /**
   * 创建dataSource
   * @returns dataSource
   */
  createDataSource() {
    let dataSource = new Cesium.CustomDataSource();
    this.viewer.dataSources.add(dataSource);
    return dataSource;
  }
  /**
   * 删除dataSource
   * @param {Object} dataSource 
   */
  removeDataSource(dataSource) {
    dataSource && this.viewer.dataSources.remove(dataSource);
  }
  /**
   * 添加entity
   * @param {Object} dataSource 
   * @param {Object} entity 
   */
  addToDataSource(dataSource, entity) {
    dataSource && dataSource.entities.add(entity);
  }
  /**
   * 清空dataSource
   * @param {Object} dataSource 
   */
  clearDataSource(dataSource) {
    dataSource && dataSource.entities.removeAll();
  }
  /**
   * 创建entity实体
   * @param {
   *  id: String, //id
   *  name: String, //点位名称
   *  properties: Object, //属性信息
   *  position: Object<Cartesian3> | Array<[lng, lat, height]> | Object<CallbackProperty> 位置信息
   *  // 方向信息
   *  orientation: {
   *    heading: Number, //偏航角
   *    pitch: Number, //俯仰角
   *    roll: Number, //横滚角
   *  } | Object<CallbackProperty>
   *  
   *  // 其他参数见创建各类型entity函数
   *  
   * } options
   */
  createEntity(options) {
    // 初始entity
    let initEntity = new Cesium.Entity({
      id: options.id,
      name: options.name,
      properties: options.properties,
      availability: options.availability,
      position: this.getPosition(options.position),
      orientation: this.getOrientation(options.position, options.orientation),
    });

    // 添加模型
    if(options.model || options.createModelEntity) {
      const createModelEntity = options.createModelEntity || this.createModelEntity.bind(this); //绑定执行的this，否则函数内获取不到this
      let modelEntity = createModelEntity(options.model);
      initEntity.merge(modelEntity);
    }

    // 添加圆形
    if(options.circle || options.createCircleEntity) {
      const createCircleEntity = options.createCircleEntity || this.createCircleEntity.bind(this);
      if(options.circle.outlineWidth && options.circle.outlineColor) {
        options.circle.center = initEntity.position.getValue();
      }
      let circleEntity = createCircleEntity(options.circle);
      initEntity.merge(circleEntity);
    }

    // 添加billboard图标
    if(options.billboard || options.createBillboardEntity) {
      const createBillboardEntity = options.createBillboardEntity || this.createBillboardEntity.bind(this);
      let billboardEntity = createBillboardEntity(options.billboard);
      initEntity.merge(billboardEntity);
    }

    // 添加label
    if(options.label || options.createLabelEntity) {
      const createLabelEntity = options.createLabelEntity || this.createLabelEntity.bind(this);
      let labelEntity = createLabelEntity(options.label);
      initEntity.merge(labelEntity);
    }

    // 添加线
    if(options.polyline || options.createPolylineEntity) {
      const createPolylineEntity = options.createPolylineEntity || this.createPolylineEntity.bind(this);
      let polylineEntity = createPolylineEntity(options.polyline);
      initEntity.merge(polylineEntity);
    }

    // 添加面
    if(options.polygon || options.createPolygonEntity) {
      const createPolygonEntity = options.createPolygonEntity || this.createPolygonEntity.bind(this);
      let polygonEntity = createPolygonEntity(options.polygon);
      initEntity.merge(polygonEntity);
    }

    return initEntity;
  }
  /**
   * 重置entity属性
   */
  resetEntity(entity, options) {
    entity.merge(this.createEntity(options));
  }
  /**
   * 获取位置信息
   * @param {Object<Cartesian3> | Array<[lng, lat, height]> | Object<CallbackProperty | SampledPositionProperty>} position 位置信息
   * @returns {Object<Cartesian3>}
   */
  getPosition(position) {
    if(Array.isArray(position)) {
      return Cesium.Cartesian3.fromDegrees(...position);
    }else {
      return position;
    }
  }
  /**
   * 获取方向信息
   * @param {Object<Cartesian3> | Array<[lng, lat, height]> | Object<CallbackProperty | VelocityOrientationProperty>} position 位置信息
   * @param {
   *   heading: Number, //偏航角
   *   pitch: Number, //俯仰角
   *   roll: Number, //横滚角
   * } orientation
   * @returns 
   */
  getOrientation(position, orientation) {
    if(orientation && orientation.constructor === Object) { //判断是普通对象
      let heading = Cesium.Math.toRadians(orientation.heading || 0);
      let pitch = Cesium.Math.toRadians(orientation.pitch || 0);
      let roll = Cesium.Math.toRadians(orientation.roll || 0);
      let hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
      return Cesium.Transforms.headingPitchRollQuaternion(
        this.getPosition(position),
        hpr
      );
    }else {
      return orientation;
    }
  }
  /**
   * 创建模型
   * @param {
   *  url: String, //模型路径
   *  scale: Number, //缩放大小 
   *  clampToGround: Boolean, //是否贴地 (可选)
   *  displayDistance: Array, //远,近处可见距离 (可选)
   * } options
   * @returns 
   */
  createModelEntity(options) {
    return new Cesium.Entity({
      model: {
        uri: options.url,
        scale: options.scale,
        heightReference: options.clampToGround ? Cesium.HeightReference.CLAMP_TO_GROUND : undefined,
        distanceDisplayCondition: options.displayDistance ? new Cesium.DistanceDisplayCondition(...options.displayDistance) : undefined,
      }
    });
  }
  /**
   * 创建圆形
   * @param {
   *  radius: Number, //半径 
   *  color: String, //颜色
   *  height: Number, //高度
   *  outlineWidth: Number, //边线宽度 (可选)
   *  outlineColor: String, //边线颜色 (可选)
   *  displayDistance: Array, //远,近处可见距离 (可选)
   * } options
   * @returns  
   */
  createCircleEntity(options) {
    // 创建圆
    let circleEntity = new Cesium.Entity({
      ellipse: {
        semiMinorAxis: options.radius,
        semiMajorAxis: options.radius,
        height: options.height,
        material: (typeof options.color === "string") ? Cesium.Color.fromCssColorString(options.color) : options.color,
        outline: false,
        numberOfVerticalLines: 512,
        distanceDisplayCondition: options.displayDistance ? new Cesium.DistanceDisplayCondition(...options.displayDistance) : undefined,
      }
    });
    // 创建圆边线
    if(options.center) {
      let outlineGeometry = new Cesium.CircleOutlineGeometry({
        center: options.center,
        radius: options.radius,
        granularity: 0.01
      });
      let circleGeometry = Cesium.CircleOutlineGeometry.createGeometry(outlineGeometry);
      let positions = circleGeometry.attributes.position.values;
      let coordinates = [].slice.call(positions);
      let posArray = [];
      for (let index = 0; index < coordinates.length; index += 3) {
        let cart3 = new Cesium.Cartesian3(coordinates[index], coordinates[index + 1], coordinates[index + 2]);
        let lnglat = this.cartesianToLnglat(cart3);
        if(options.height) lnglat.height = options.height; //圆设置固定高度时，设置边线高度
        posArray.push(Object.values(lnglat));
      }
      // 添加第一个坐标，首尾相连
      let firstCoord = this.cartesianToLnglat(new Cesium.Cartesian3(coordinates[0], coordinates[1], coordinates[2]));
      if(options.height) firstCoord.height = options.height;
      posArray.push(Object.values(firstCoord));
      // 创建线
      let polylineEntity = this.createPolylineEntity({
        coordinates: posArray,
        width: options.outlineWidth,
        color: options.outlineColor,
        clampToGround: !options.height,
        displayDistance: options.displayDistance
      });
      // entity合并
      circleEntity.merge(polylineEntity);
    }
    return circleEntity;
  }
  /**
   * 创建图标
   * @param {
   *  image: String, //图片路径
   *  width: Number, //宽度 
   *  height: Number, //高度 
   *  clampToGround: Boolean, //是否贴地 (可选)
   *  verticalOrigin: Number, //竖直对齐方式 ["CENTER", "BOTTOM",  "BASELINE", "TOP"] (可选，默认"BOTTOM")
   *  horizontalOrigin: Number, //水平对齐方式 ["CENTER", "LEFT",  "RIGHT"] (可选，默认"CENTER")
   *  offset: Array, //x,y偏移 (可选)
   *  displayDistance: Array, //远,近处可见距离 (可选)
   *  scaleDistance: Array, //远,近处缩放大小 (可选)
   * } options
   * @returns  
   */
  createBillboardEntity(options) {
    return new Cesium.Entity({
      billboard: {
        image: options.image,
        width: options.width,
        height: options.height,
        heightReference: options.clampToGround ? Cesium.HeightReference.CLAMP_TO_GROUND : undefined,
        verticalOrigin: options.verticalOrigin ? Cesium.VerticalOrigin[options.verticalOrigin] : Cesium.VerticalOrigin.BOTTOM,
        horizontalOrigin: options.horizontalOrigin ? Cesium.HorizontalOrigin[options.horizontalOrigin] : Cesium.HorizontalOrigin.CENTER,
        pixelOffset: options.offset ? new Cesium.Cartesian2(...options.offset) : undefined,
        distanceDisplayCondition: options.displayDistance ? new Cesium.DistanceDisplayCondition(...options.displayDistance) : undefined,
        scaleByDistance: options.scaleDistance ? new Cesium.NearFarScalar(...options.scaleDistance): undefined,
      }
    });
  }
  /**
   * 创建文本
   * @param {
   *  text: String, //文本
   *  size: Number, //字体大小 (可选，默认12)
   *  color: String, //字体颜色 (可选，默认#ffffff)
   *  backgroundColor: Boolean, //背景色 (可选)
   *  clampToGround: Boolean, //是否贴地 (可选)
   *  offset: Array, //x,y偏移 (可选)
   *  displayDistance: Array, //远,近处可见距离 (可选)
   *  scaleDistance: Array, //远,近处缩放大小 (可选)
   * } options 
   * @returns 
   */
  createLabelEntity(options) {
    return new Cesium.Entity({
      label: {
        text: String(options.text),
        font: (options.size || 12) +'px monospace',
        fillColor: Cesium.Color.fromCssColorString(options.color || "#ffffff"),
        showBackground: Boolean(options.backgroundColor),
        backgroundColor: options.backgroundColor ? Cesium.Color.fromCssColorString(options.backgroundColor) : undefined,
        heightReference: options.clampToGround ? Cesium.HeightReference.CLAMP_TO_GROUND : undefined,
        verticalOrigin : Cesium.VerticalOrigin.BOTTOM,
        horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
        pixelOffset: options.offset ? new Cesium.Cartesian2(...options.offset) : undefined,
        distanceDisplayCondition: options.displayDistance ? new Cesium.DistanceDisplayCondition(...options.displayDistance) : undefined,
        scaleByDistance: options.scaleDistance ? new Cesium.NearFarScalar(...options.scaleDistance): undefined,
      }
    });
  }
  /**
   * 创建线
   * @param {
   *  coordinates: Array | CallbackProperty, [[x, y]] / [[x, y, z]] / [Cartesian3, Cartesian3] / CallbackProperty:()=>[Cartesian3, Cartesian3] //坐标值
   *  width: Number, //宽度
   *  color: String, //颜色
   *  clampToGround: Boolean, //是否贴地 (可选)
   *  displayDistance: Array, //远,近处可见距离 (可选)
   * } options
   * @returns 
   */
  createPolylineEntity(options) {
    let positions;
    // 回调属性
    if(options.coordinates instanceof Cesium.CallbackProperty) {
      positions = options.coordinates;
    }
    // 数组坐标值
    else if(Array.isArray(options.coordinates[0])) {
      let coords = [];
      options.coordinates.forEach(item => {
        coords.push(...item);
      });
      if(options.coordinates[0].length === 2) {
        positions = Cesium.Cartesian3.fromDegreesArray(coords);
      }else if(options.coordinates[0].length === 3){
        positions = Cesium.Cartesian3.fromDegreesArrayHeights(coords);
      }
    }
    // 三维空间坐标值
    else if(options.coordinates[0] instanceof Cesium.Cartesian3) {
      positions = options.coordinates;
    }
    return new Cesium.Entity({
      polyline: {
        positions,
        width: options.width,
        material: (typeof options.color === "string") ? Cesium.Color.fromCssColorString(options.color) : options.color,
        clampToGround: options.clampToGround, //设置贴地后，每个点的高度不生效
        distanceDisplayCondition: options.displayDistance ? new Cesium.DistanceDisplayCondition(...options.displayDistance) : undefined,
      },
    });
  }
  /**
   * 创建多边形
   * @param {
   *  coordinates: Array | CallbackProperty, //坐标值 [[[x, y], [x, y]]] / [[[x, y, z], [x, y, z]]] / [Cartesian3, Cartesian3] / CallbackProperty:()=>[Cartesian3, Cartesian3]
   *  color: String, //颜色
   *  height: Number,//高度 (可选)
   *  extrudedHeight:  Number,//拉伸高度 (可选)
   *  outlineColor: String, //外边框颜色 (可选)
   *  outlineWidth: Number, //外边框宽度 (可选)
   *  displayDistance: Array, //远,近处可见距离 (可选)
   * } options 
   * @returns 
   */
  createPolygonEntity(options) {
    let positions;
    // 回调属性
    if(options.coordinates instanceof Cesium.CallbackProperty) {
      positions = options.coordinates;
    }
    // 数组坐标值
    else if(Array.isArray(options.coordinates[0])) {
      let coords = [];
      options.coordinates[0].forEach(item => {
        coords.push(...item);
      });
      if(options.coordinates[0][0].length === 2) {
        positions = Cesium.Cartesian3.fromDegreesArray(coords);
      }else if(options.coordinates[0][0].length === 3){
        positions = Cesium.Cartesian3.fromDegreesArrayHeights(coords);
        // 设置每个坐标点的高度
        options.perPositionHeight = true;
      }
    }
    // 三维空间坐标值
    else if(options.coordinates[0] instanceof Cesium.Cartesian3) {
      positions = new Cesium.PolygonHierarchy(options.coordinates);
    }
    let polygonEntity = new Cesium.Entity({
      polygon: {
        hierarchy: positions,
        material: (typeof options.color === "string") ? Cesium.Color.fromCssColorString(options.color) : options.color,
        perPositionHeight: options.perPositionHeight, //每个坐标点单独设置高度
        height: options.height,
        extrudedHeight: options.extrudedHeight,
        outline: false,
        distanceDisplayCondition: options.displayDistance ? new Cesium.DistanceDisplayCondition(...options.displayDistance) : undefined,
      }
    });
    //设置面的边线样式
    if(options.outlineWidth && options.outlineColor) {
      let linePositions;
      // 回调属性
      if(options.coordinates instanceof Cesium.CallbackProperty) {
        linePositions = new Cesium.CallbackProperty(() => {
          let positions = polygonEntity.polygon.hierarchy.getValue().positions;
          //面设置固定高度时，线设置对应高度
          if(!options.perPositionHeight && options.height) {
            return positions.map(item => {
              let { lng, lat } = this.cartesianToLnglat(item);
              return this.lnglatToCartesian(lng, lat, options.height);
            });
          }
          return positions;
        }, false);
      }
      // 数组坐标值
      else if(Array.isArray(options.coordinates)) {
        if(!options.perPositionHeight && options.height) { //面设置固定高度时，线设置对应高度(设置perPositionHeight时，height不生效)
          let coords = [];
          options.coordinates[0].forEach(item => {
            coords.push(...item, options.height);
          });
          linePositions = Cesium.Cartesian3.fromDegreesArrayHeights(coords);
        }else { //取面的坐标值
          linePositions = polygonEntity.polygon.hierarchy.getValue().positions;
        }
      }
      
      let polylineEntity = new Cesium.Entity({
        polyline: {
          positions: linePositions,
          width: options.outlineWidth,
          material: (typeof options.outlineColor === "string") ? Cesium.Color.fromCssColorString(options.outlineColor) : options.outlineColor,
          clampToGround: !options.height && !options.perPositionHeight,
          distanceDisplayCondition: options.displayDistance ? new Cesium.DistanceDisplayCondition(...options.displayDistance) : undefined,
        }
      });
      // entity合并
      polygonEntity.merge(polylineEntity);
    }
    return polygonEntity;
  }
  /**
   * 根据属性值获取entity
   * @param {*} dataSource 数据源/图层
   * @param {*} field 字段
   * @param {*} value 字段值
   */
  getEntityByProperty(dataSource, field, value) {
    return dataSource.entities.values.find(entity => {
      let properties = this.resetCesiumProperty(entity.properties);
      return properties[field] == value;
    });
  }
  /**
   * 获取entity中心点
   * @param {Object} entity 
   * @returns 
   */
  getEntityCenter(entity) {
    if(!entity) return;
    if(entity.position) {
      let cartesian = entity.position.getValue();
      let { lng, lat } = this.cartesianToLnglat(cartesian);
      let height = this.getHeight(lng, lat);
      return this.lnglatToCartesian(lng, lat, height);
    }else if(entity.polygon) {
      let positions = entity.polygon.hierarchy.getValue().positions;
      if(positions.length < 3) return; //未形成面
      let coords = positions.map(item => {
        let {lng, lat} = this.cartesianToLnglat(item);
        return [lng, lat];
      });
      coords.push(coords[0]); //防止cesium面点位不闭合
      try {
        let polygon = turf.polygon([coords]);
        let centroid = turf.centroid(polygon);
        let coordinates = centroid.geometry.coordinates;
        let height = this.getHeight(...coordinates);
        return this.lnglatToCartesian(...coordinates, height);
      } catch (error) {
        console.log('log--- error', error);
        return;
      }
    }else if(entity.polyline) {
      let positions = entity.polyline.positions.getValue();
      let cartesianCenter;
      let length = positions.length;
      if(length % 2 === 0) { //偶数点
        // 如length = 6, length / 2 = 3， positions[3-1]为中间左侧值，positions[3]为中间右侧值
        let left = positions[length / 2 - 1];
        let right = positions[length / 2];
        // 获取两点的中心点坐标
        cartesianCenter = Cesium.Cartesian3.midpoint(left, right, new Cesium.Cartesian3);
      }else {
        // 如length = 5, length / 2 = 2.5， Math.floor向下取整为2，positions[2]则为中间数据
        cartesianCenter = positions[Math.floor(length / 2)];
      }
      let { lng, lat } = this.cartesianToLnglat(cartesianCenter);
      let height = this.getHeight(lng, lat);
      return this.lnglatToCartesian(lng, lat, height);
    }
  }
  /**
   * 添加geojson数据
   * @param {*} url 数据地址/geojson数据
   * @param {*} options 样式配置 {
   *   clampToGround: 是否贴地
   *   fill: 填充色
   *   stroke: 线颜色
   *   strokeWidth: 线宽
   *   markerSymbol: 图标符号
   *   markerSize: 图标大小
   *   markerColor: 图标颜色
   * }
   * @param {*} isAdd 是否直接添加到地图
   * @returns 
   */
  async addGeojsonDataSource(url, options, isAdd) {
    let dataSource = await Cesium.GeoJsonDataSource.load(url, options);
    if(isAdd) this.viewer.dataSources.add(dataSource);
    return dataSource;
  }
  /**
   * 添加Kml数据
   * @param {*} url 数据地址/Kml数据
   * @param {*} isAdd 是否直接添加到地图
   * @returns 
   */
  async addKmlDataSource(url, options = {clampToGround: true}, isAdd) {
    let dataSource = await Cesium.KmlDataSource.load(url, options);
    if(isAdd) this.viewer.dataSources.add(dataSource);
    return dataSource;
  }
  /**
   * geojson转entities
   * @param {Object} geojson geojson对象
   * @param {Object|callback} createOption 同createEntity的参数
   * @param {Boolean} clampToGround 是否贴地，默认为true
   * @returns Promise.resolve(Array<entity>)
   */
  async geojsonToEntities(geojson, createOption, clampToGround = true) {
    let dataSource = await Cesium.GeoJsonDataSource.load(geojson, {clampToGround});
    let entities = dataSource.entities.values;
    let options = createOption;
    return entities.map(item => {
      // createOption定义为回调函数时，回调参数为每个entity，可根据entity属性，返回新的options配置
      if(typeof createOption === "function") {
        options = createOption(item);
      }
      // 点、标注等坐标类的
      if(item.position) {
        item.billboard = undefined; //清除默认的billboard图标
        options.position = item.position;
      }
      // 线
      else if(item.polyline && item.polyline.positions) {
        options.polyline.coordinates = item.polyline.positions.getValue();
      }
      // 面
      else if(item.polygon && item.polygon.hierarchy) {
        options.polygon.coordinates = item.polygon.hierarchy.getValue().positions;
      }
      let entity = this.createEntity(options);
      // 合并其他属性值
      entity.merge(item);
      return entity;
    });
  }
  /**
   * 添加动态扩散圆
   * @param {*} options {
   *  layer 添加到图层
   *  minRange 扩散最小半径
   *  maxRange 扩散最大半径
   *  interval 每次增加的大小
   *  image 扩散展示图片
   *  position 位置
   * }
   */
  addSpreadCircle(options) {
    let layer = options.layer || this.viewer;
    let minRange = options.minRange;
    let maxRange = options.maxRange;
    let interval = options.interval;
    let initRange = options.minRange;
    let image = options.image || require('./imgs/circle.png');
    let position = options.position;
  
    function changeRange() {
      initRange = initRange + interval;
      if(initRange >= maxRange){
        initRange = minRange;
      }
      return initRange;
    }
    function changeColor() {
      let opacity = 1 - initRange / maxRange;
      return Cesium.Color.WHITE.withAlpha(opacity);
    }
    let entity = new Cesium.Entity({
      position: Cesium.Cartesian3.fromDegrees(...position),
      ellipse: {
        semiMinorAxis: new Cesium.CallbackProperty(changeRange, false),
        semiMajorAxis: new Cesium.CallbackProperty(changeRange, false),
        material: new Cesium.ImageMaterialProperty({
          image: image,
          transparent: true,
          color: new Cesium.CallbackProperty(changeColor, false)
        })
      }
    });
    layer.entities.add(entity);
    return entity;
  }
  /**
   * 添加动态扫描圆
   * @param {*} options {
   *  layer 添加到图层
   *  radius 圆的半径
   *  interval 每次旋转角度
   *  image 扩散展示图片
   *  position 位置
   * }
   */
   addScanCircle(options) {
    let layer = options.layer || this.viewer; //图层
    let position = options.position; //位置
    let radius = options.radius; //圆的半径
    let interval = options.interval || 0.05; // 每次旋转角度
    let image = options.image || require('./imgs/scan.png'); //图片

    let rotation = Cesium.Math.toRadians(30);
    function rotete(){
      rotation -= interval;
      return rotation;
    }
    let entity = new Cesium.Entity({
      position: Cesium.Cartesian3.fromDegrees(...position),
      ellipse: {
        semiMinorAxis: radius,
        semiMajorAxis: radius,
        stRotation: new Cesium.CallbackProperty(rotete, false),
        material: new Cesium.ImageMaterialProperty({
          image: image,
          repeat: new Cesium.Cartesian2(1.0, 1.0),
          transparent: true,
        }),
      },
    });
    layer.entities.add(entity);
    return entity;
  }
  /**
   * 添加遮罩层
   * @param {Object} options {
   *  coordinates: Array, //中心区域
   *  maskColor: String, //遮罩颜色
   *  lineWidth: Number,//宽度
   *  lineColor: String, //边线颜色
   *}
   * @returns 
   */
  addMaskEntity(options) {
    // 遮罩区域
    let polygonEntity = new Cesium.Entity({
      polygon: {
        hierarchy: {
          // 添加外部区域为1/4半圆，设置为180会报错
          positions: Cesium.Cartesian3.fromDegreesArray([0, 0, 0, 90, 179, 90, 179, 0]),
          // 中心挖空的“洞”
          holes: [{
            positions: Cesium.Cartesian3.fromDegreesArray(options.coordinates)
          }]
        },
        material: Cesium.Color.fromCssColorString(options.maskColor),
        height: 0, //添加height属性，防止显示遮罩边界bug
      }
    });
    // 边界线
    let lineEntity = new Cesium.Entity({
      polyline: {
        positions: Cesium.Cartesian3.fromDegreesArray(options.coordinates),
        width: options.lineWidth,
        material: Cesium.Color.fromCssColorString(options.lineColor),
      }
    });
    this.viewer.entities.add(polygonEntity);
    this.viewer.entities.add(lineEntity);
  }
  /**
   * 添加三维模型
   * @param {*} option {
   *  url: String, 模型地址
   *  splitDirection: String 模型分平方向 ["LEFT", "RIGHT"]
   *  lng: Number, 模型经度（调整模型位置）
   *  lat: Number, 模型纬度（调整模型位置）
   *  hight: Number, 模型高度（模型不贴地问题）
   *  styleColor: Object 设置颜色样式
   *  maximumScreenSpaceError: Number 最大屏幕误差
   *  transformCallback: Function 转换矩阵回调（返回平移后的矩阵）
   * }
   * @returns 
   */
  add3DTileset(option) {
    let tileset3d = new Cesium.Cesium3DTileset({
      url: option.url,
      splitDirection: option.splitDirection ? Cesium.SplitDirection[option.splitDirection] : Cesium.SplitDirection.NONE,
      skipLevelOfDetail: true,
      baseScreenSpaceError: 1024,
      maximumScreenSpaceError: option.maximumScreenSpaceError || 16, // 数值加大，能让最终成像变模糊（默认16，效果比较好）
      skipScreenSpaceErrorFactor: 16,
      skipLevels: 1,
      immediatelyLoadDesiredLevelOfDetail: false,
      loadSiblings: true, // 如果为true则不会在已加载完概况房屋后，自动从中心开始超清化房屋
      cullWithChildrenBounds: true,
      cullRequestsWhileMoving: true,
      cullRequestsWhileMovingMultiplier: 10, // 值越小能够更快的剔除
      // preloadWhenHidden: true, //tileset.show是false时，也去预加载数据
      preferLeaves: true,
      maximumMemoryUsage: 512, // 内存分配变小有利于倾斜摄影数据回收，提升性能体验
      progressiveResolutionHeightFraction: 0.5, // 数值偏于0能够让初始加载变得模糊
      dynamicScreenSpaceErrorDensity: 0.5, // 数值加大，能让周边加载变快
      dynamicScreenSpaceErrorFactor: 1, // 
      dynamicScreenSpaceError: true, // 根据测试，有了这个后，会在真正的全屏加载完之后才清晰化房屋
      debugShowBoundingVolume: Boolean(option.debugShowBoundingVolume), //调试包围盒
    });
    tileset3d.readyPromise.then(async (tileset) => {
      // 设置模型高度
      if(option.height != undefined) {
        if(option.height === 0) {
          // 获取包围盒矩阵
          let halfAxes = tileset._root._boundingVolume._orientedBoundingBox.halfAxes;
          // 获取包围盒中心点
          let boxCenter = tileset._root._boundingVolume._orientedBoundingBox.center;
          // 包围盒中心点高度
          let centerHeight = this.cartesianToLnglat(boxCenter).height;
          // 计算x，y，z坐标
          let xaxis = Cesium.Matrix3.getColumn(halfAxes, 0, new Cesium.Cartesian3());
          let yaxis = Cesium.Matrix3.getColumn(halfAxes, 1, new Cesium.Cartesian3());
          let zaxis = Cesium.Matrix3.getColumn(halfAxes, 2, new Cesium.Cartesian3());

          let calcXaxis = new Cesium.Cartesian3();
          let calcYaxis = new Cesium.Cartesian3();
          let calcZaxis = new Cesium.Cartesian3(); // 左下角坐标
          // 计算包围盒左下角坐标
          Cesium.Cartesian3.subtract(boxCenter, xaxis, calcXaxis);
          Cesium.Cartesian3.subtract(calcXaxis, yaxis, calcYaxis);
          Cesium.Cartesian3.subtract(calcYaxis, zaxis, calcZaxis);
          // 获取包围盒底部高度
          let bettomLeft = Cesium.Cartographic.fromCartesian(calcZaxis);
          // 包围盒底部高度
          let boxHeight = bettomLeft.height;
          // 设置模型高度 
          if(centerHeight < 0) {
            // 模型低于地面，计算模型底部到地面的高度
            option.height = -boxHeight;
          }else {
            // 模型高度大于地面高度，计算包围盒 中心点 到 包围盒底部的高度
            option.height = -(centerHeight - boxHeight);
          }
          if(this.mapConfig.isTerrain) {
            // 获取包围盒底部坐标的地面高度
            let terrainCartographics = await new Cesium.sampleTerrain(this.viewer.terrainProvider, 13, [bettomLeft]);
            // 模型设置高度 加上地形高度
            option.height = option.height + terrainCartographics[0].height;
          }
        }
        let transform = tileset._root.transform;
        let boundingSphere = tileset.boundingSphere;
        let cartographic = Cesium.Cartographic.fromCartesian(boundingSphere.center);
        let surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0);
        let offset;
        if(option.lng && option.lat) {
          offset = Cesium.Matrix4.multiplyByPoint(transform, Cesium.Cartesian3.fromDegrees(option.lng, option.lat, option.height), new Cesium.Cartesian3(0,0,0));
        }else {
          offset = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, option.height);
        }
        let translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3());
        let transMatrix = Cesium.Matrix4.fromTranslation(translation);
        Cesium.Matrix4.multiply(transform, transMatrix, transform);
        tileset._root.transform = transform; //设置矩阵
        if(option.transformCallback) { //返回平移后的矩阵
          option.transformCallback(transform);
        }
      }
      if(option.styleColor) {
        tileset.style = new Cesium.Cesium3DTileStyle({
          color: option.styleColor
        });
      }
      this.viewer.scene.primitives.add(tileset);
    }).catch(err => {
      console.log('log--- err', err);
    });
    return tileset3d;
  }
  /**
   * 模型旋转
   * @param {*} tileset 
   * @param {*} options 
   * @returns 
   */
  rotate3dTiles(tileset, options) {
    let transform = tileset._root.transform;
    let _rotateX = Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(options.x));
    let _rotateY = Cesium.Matrix3.fromRotationY(Cesium.Math.toRadians(options.y));
    let _rotateZ = Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(options.z));
    transform = Cesium.Matrix4.multiplyByMatrix3(transform, _rotateX, transform);
    transform = Cesium.Matrix4.multiplyByMatrix3(transform, _rotateY, transform);
    transform = Cesium.Matrix4.multiplyByMatrix3(transform, _rotateZ, transform);
    tileset._root.transform = transform;
  }
  /**
   * 创建自定义shader
   * @param {*} shadeText 
   * @returns 
   */
  createCustomShader(shadeText) {
    return new Cesium.CustomShader({
      lightingModel: Cesium.LightingModel.UNLIT,
      fragmentShaderText: shadeText,
    });
  }
  /**
   * 删除primitive
   * @param {*} primitive 
   * @returns 
   */
  removePrimitive(primitive) {
    this.viewer.scene.primitives.remove(primitive);
  }
  /**
   * 添加例子风场效果
   * @param {*} options {
  *   blobFile 读取的NetCDF的".nc"类型的文件 .nc -> blob
  *   windParams: {
  *    maxParticles: Number, 最大粒子数(会自动取平方数)
  *    particleHeight: Number 粒子高度
  *    fadeOpacity: Number, 拖尾透明度
  *    dropRate: Number, 粒子重置率
  *    dropRateBump: Number, 随速度增加的粒子重置率百分比，速度越快越密集
  *    speedFactor: Number 粒子速度
  *    lineWidth: Number 线宽
  *    dynamic: Boolean 是否动态运行
  *   }
  *   fields 设置自定义字段 defaultFields = { U: 'U', V: 'V', W: '', H: '', lon: 'lon', lat: 'lat', lev: '' }
  *   colorTable 颜色值 defaultColorTable = [[[1.0, 1.0, 1.0]]]
   * }
   * @returns 
   */
  addParticleWindy(options) {
    let particle3D = new Particle3D(this.viewer, {
      input: options.blobFile,
      userInput: options.windParams,
      fields: options.fields,
      colorTable: options.colorTable,
    });
    // 启动粒子系统
    particle3D.init().then(() => {
      particle3D.show(); // 开始运行粒子系统
    });
    return particle3D;
  }
  /**
   * 鼠标点击
   * @param {Function} callback 返回点击获取数据
   * @param {*} mode 左键/右键
   * @returns 
   */
  mapOnClick(callback, mode = "LEFT_CLICK") {
    const viewer = this.viewer;
    let clickHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    clickHandler.setInputAction((movement) => {
      let position = movement.position || movement.endPosition;
      let pickEntity = viewer.scene.pick(position);
      let entity = pickEntity && pickEntity.id;
      // 获取点击地面坐标
      let cartesian = this.drawer.getCatesian3FromPX(position);
      let {lng, lat, height} = this.cartesianToLnglat(cartesian);
      // 获取点击点线面的中心点
      let center = this.getEntityCenter(entity);
      if(callback) callback({
        lng,
        lat,
        height,
        cartesian,
        center,
        entity,
      });
    }, Cesium.ScreenSpaceEventType[mode]);
    return clickHandler;
  }
  /**
   * 鼠标点击获取多个entity
   * @param {Function} callback 返回点击获取数据
   * @param {*} mode 左键/右键
   * @returns 
   */
  mapOnMultiClick(callback, mode = "LEFT_CLICK") {
    const viewer = this.viewer;
    let clickHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    clickHandler.setInputAction((movement) => {
      let position = movement.position || movement.endPosition;
      let pickEntity = viewer.scene.drillPick(position);//获取多个点击物体
      let entities = pickEntity.map(v => v.id);
      // 获取点击地面坐标
      let cartesian = this.drawer.getCatesian3FromPX(position);
      let {lng, lat, height} = this.cartesianToLnglat(cartesian);
      if(callback) callback({
        lng,
        lat,
        height,
        cartesian,
        entities,
      });
    }, Cesium.ScreenSpaceEventType[mode]);
    return clickHandler;
  }
  /**
   * 鼠标移动
   * @param {Function} callback 返回点击获取数据
   * @returns 
   */
  mapOnMove(callback) {
    const viewer = this.viewer;
    let moveHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    moveHandler.setInputAction((movement) => {
      let position = movement.position || movement.endPosition;
      let pickEntity = viewer.scene.pick(position);
      let entity = pickEntity && pickEntity.id;
      // 获取点击地面坐标
      let cartesian = this.drawer.getCatesian3FromPX(position);
      let {lng, lat, height} = this.cartesianToLnglat(cartesian);
      let center = this.getEntityCenter(entity);
      if(callback) callback({
        lng,
        lat,
        height,
        cartesian,
        center,
        entity,
      });
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    return moveHandler;
  }
  /**
   * 销毁事件
   * @param {Object} handler 
   */
  destroyHandler(handler) {
    handler && handler.destroy();
  }
  /**
   * 监听地图实时渲染事件
   * @param {Function} callback({
   *    height 相机高度
   * }) 
   * @returns event.removeEventListener()
   */
  listenRenderEvent(callback) {
    return this.viewer.scene.preRender.addEventListener(() => {
      let { positionCartographic } = this.viewer.scene.camera;
      //获取当前相机高度
      let height = Math.ceil(positionCartographic.height);
      callback && callback({
        height
      });
    });
  }
  /**
   * 计算两点间距离
   * @param {Array} startCoords [lng, lat]
   * @param {Array} endCoords [lng, lat]
   * @returns 
   */
  getDistance(startCoords, endCoords){
    let satrt = Cesium.Cartographic.fromDegrees(...startCoords);
    let end = Cesium.Cartographic.fromDegrees(...endCoords);
    let geodesic = new Cesium.EllipsoidGeodesic();
    geodesic.setEndPoints(satrt, end);
    let distance = geodesic.surfaceDistance;
    return distance;
  }
  /**
   * 计算线的长度
   * @param {Object} entity
   * @returns 
   */
  getLength(entity) {
    let distance = 0;
    let positions = entity.polyline.positions.getValue();
    for (let i = 0; i < positions.length - 1; i++) {
      let point1cartographic = Cesium.Cartographic.fromCartesian(positions[i]);
      let point2cartographic = Cesium.Cartographic.fromCartesian(positions[i + 1]);
      //根据经纬度计算出距离
      let geodesic = new Cesium.EllipsoidGeodesic();
      geodesic.setEndPoints(point1cartographic, point2cartographic);
      let s = geodesic.surfaceDistance;
      //返回两点之间的距离
      s = Math.sqrt(Math.pow(s, 2) + Math.pow(point2cartographic.height - point1cartographic.height, 2));
      distance = distance + s;
    }
    return distance;
  }
  /**
   * 计算面积
   * @param {Object} entity
   * @returns 
   */
  getArea(entity){
    let positions = entity.polygon.hierarchy.getValue().positions;
    let coordinates = positions.map(item => this.cartesianToLnglat(item));
    let result = 0;
    if (coordinates) {
      let h = 0;
      let ellipsoid = Cesium.Ellipsoid.WGS84;
      coordinates.push(coordinates[0]);
      for (let i = 1; i < coordinates.length; i++) {
        let oel = ellipsoid.cartographicToCartesian(
          Cesium.Cartographic.fromDegrees(coordinates[i - 1].lng, coordinates[i - 1].lat, coordinates[i - 1].height)
        )
        let el = ellipsoid.cartographicToCartesian(
          Cesium.Cartographic.fromDegrees(coordinates[i].lng, coordinates[i].lat, coordinates[i].height)
        )
        h += oel.x * el.y - el.x * oel.y;
      }
      result = Math.abs(h).toFixed(2);
    }
    return result;
  }
  /**
   * 获取高度（贴合模型或地面高度）
   * @param {*} lng | cartesian
   * @param {*} lat 
   * @returns 
   */
  getHeight(lng, lat) {
    let cartographic = Cesium.Cartographic.fromDegrees(lng, lat);
    // 先获取模型高度，获取不到则 获取地面高度
    let height = this.viewer.scene.sampleHeight(cartographic);
    //小于最低海拔，获取模型高度错误（可能原因：物体不在视野范围，获取到模型高度近似地球半径：-6343689.232611479）
    if(!height || (height && height < -11034)) {
      /**
       * 注意：
       * 相机高度太高时，获取高度值有偏差
       */
      height = this.viewer.scene.globe.getHeight(cartographic) || 0;
    }
    return height;
  }
  /**
   * 世界坐标转经纬度
   * @param {Object} cartesian 
   * @returns 
   */
  cartesianToLnglat(cartesian) {
    if(!cartesian) return {};
    let globe = this.viewer.scene.globe;
    let cartographic = globe.ellipsoid.cartesianToCartographic(cartesian);
    let lng = Cesium.Math.toDegrees(cartographic.longitude);
    let lat = Cesium.Math.toDegrees(cartographic.latitude);
    let height = cartographic.height;
    return {lng, lat, height};
  }
  /**
   * 经纬度转世界坐标
   * @param {Number} lng 
   * @param {Number} lat 
   * @param {Number} height 
   * @returns 
   */
  lnglatToCartesian(lng, lat, height = 0) {
    return Cesium.Cartesian3.fromDegrees(lng, lat, height);
  }
  // WKT转geojson
  wktToGeojson(wkt) {
    return WKT.parse(wkt);
  }
  // geojson转wkt
  geojsonToWkt(geojson) {
    return WKT.stringify(geojson);
  }
  /**
   * cesium properties对象转为普通对象
   * @param {Object} properties 
   * @returns 
   */
  resetCesiumProperty(properties) {
    if(properties && properties.propertyNames) {
      let obj = {};
      properties.propertyNames.forEach(item => {
        obj[item] = properties[item].getValue();
      });
      return obj;
    }
  }
  // 销毁地图
  destroy() {
    this.viewer.destroy();
    this.viewer = null;
  }
}

export default MapClass;